
/**
 * @namespace utils.ArraysConstructor
 * Created with IntelliJ IDEA.
 * User: zhengry
 * Date: 2014/11/17
 * Time: 8:28
 */
define(['angular'], function (angular) {
    return function ($filter) {
        return {
            /**
             * 构建树数据
             * @description 请尽量使用后端构建树数据， 在js中构建树数据是低效的做法
             * @param {Array} source 源数据
             * @param {Object} [config] 配置项
             * @param {String} [config.prop = 'id'] 元素的主要属性名称,与父属性作比较的属性的名称
             * @param {String} [config.parentProp = 'parentId'] 父属性名称
             * @param {String} [config.childrenProp = 'children'] 子集属性名称
             * @return {Array}
             */
            buildTree: function (source, config) {
                source = angular.copy(source);
                config = config || {};
                var prop = angular.isString(config.prop) ? config.prop : 'id',
                    parentProp = angular.isString(config.parentProp) ? config.parentProp : 'parentId',
                    childrenProp = angular.isString(config.childrenProp) ? config.childrenProp : 'children',
                    results = [],
                    me = this,
                    fetchChildrenFn = function (element, margin) {
                        if (margin.length === 0) return;
                        var children = [],
                            i, len, el, childrenMargin;
                        for (i = 0, len = margin.length; i < len; i++) {
                            el = margin[i];
                            if (el[parentProp] === element[prop]) {
                                children.push(el);
                            }
                        }
                        childrenMargin = me.subtract(margin, children);
                        for (i = 0, len = children.length; i < len; i++) {
                            el = children[i] = angular.copy(children[i]);
                            fetchChildrenFn(el, childrenMargin);
                        }
                        if (children.length > 0) {
                            element[childrenProp] = children;
                        }
                    },
                    margin, i, len, el;
                for (i = 0, len = source.length; i < len; i++) {
                    el = source[i];
                    if (!el[parentProp]) {
                        results.push(el);
                    }
                }
                margin = me.subtract(source, results);
                // copy放在下面是为了subtract使用'==='而不是equals， 效率更高
                for (i = 0, len = results.length; i < len; i++) {
                    el = results[i] = angular.copy(results[i]);
                    fetchChildrenFn(el, margin);
                }
                return results;
            },
            /**
             * @deprecated 请使用 `angular.filters.orderBy`, 本方法仅作为简单排序
             * @param {Array} source 源数组，本方法不改变此数组
             * @param {string/Function/String[]/Function[]} [predicate] 比较器， 可用Array传入多个， 会按照顺序排序
             * @param {boolean} [reverse = false] 反转, 默认升序
             * @return {Array} 返回排序后的新数组
             */
            orderBy: function (source, predicate, reverse) {
                return $filter('orderBy')(source, predicate, reverse);
            },
            /**
             * 抽取源数组中对象的一个属性成一个新的数组
             * @param {Array} source 源数组
             * @param {String} prop 抽取的属性名称
             * @return {Array} 抽取后结果
             * @deprecated @see Arrays.extract
             */
            extractToArray: function (source, prop) {
                var results = [],
                    i, len, el;
                if (angular.isArray(source))  {
                    for (i = 0, len = source.length; i < len; i++) {
                        el = source[i];
                        if (el && el.hasOwnProperty(prop)) {
                            results[i] = el[prop];
                        }
                    }
                }
                return results;
            },

            /**
             * 抽取源数组中对象的一个属性成一个新的数组
             * @param {Array} source 源数组
             * @param {String|Function} extractor
             * @return {Array} 抽取后结果
             */
            extract: function (source, extractor) {
                var results = [],
                    extractorIsFn = angular.isFunction(extractor),
                    i, len, el;
                if (angular.isArray(source))  {
                    for (i = 0, len = source.length; i < len; i++) {
                        el = source[i];
                        if (el) {
                            if (extractorIsFn) {
                                results[i] = extractor(el);
                            } else if (el.hasOwnProperty(extractor)) {
                                results[i] = el[extractor];
                            }
                        }
                    }
                }
                return results;
            },

            /**
             * 抽取源数组中对象的两个属性， 组合成Map (对象字面量)
             * @param {Array} source 源数组
             * @param {String} keyProp 要提取为Map中的Key值的属性名
             * @param {String} valProp 要提取为Map中的Value值的属性名
             * @return {Object} 抽取后的结果
             */
            extractToMap: function (source, keyProp, valProp) {
                var results = {},
                    i, len, el;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        el = source[i];
                        if (el && el.hasOwnProperty(keyProp) && el.hasOwnProperty(valProp)) {
                            results[source[i][keyProp]] = source[i][valProp];
                        }
                    }
                }
                return results;
            },
            /**
             * 返回 `a` 数组扣除`b`数组中元素的结果
             */
            subtract: function (a, b) {
                var results = [],
                    i = 0,
                    len = a.length,
                    el;
                if (angular.isArray(a) && angular.isArray(b)) {
                    for (; i < len; i++) {
                        el = a[i];
                        if (!this.exists(b, el)) {
                            results.push(el);
                        }
                    }
                }
                return results;
            },
            /**
             * 判断源数组中是否存在某元素， 使用`angular.equals`判断
             * @param {Object} source 源数组
             * @param {String} element 元素
             */
            exists: function (source, element) {
                var source = angular.isArray(source) ? source : [],
                    i = 0,
                    len = source.length;
                if (angular.isArray(source)) {
                    for (; i < len; i++) {
                        if (angular.equals(element, source[i])) {
                            return true;
                        }
                    }
                }
                return false;
            },

            /**
             * 过滤源数组， 返回匹配的元素数组
             * @param {Array} source 源数组
             * @param {function(Object):boolean} predicate 匹配器
             * @return {Array}
             */
            filter: function (source, predicate) {
                var results = [],
                    i, len, element;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        element = source[i];
                        if (predicate(element) === true) {
                            results.push(element);
                        }
                    }
                }
                return results;
            },

            /**
             * 在源数组中使用匹配器查找， 返回第一个找到的元素
             * @param {Array} source 源数组
             * @param {function(Object):boolean} predicate 匹配器
             * @return {Object} 第一个找到的元素
             */
            findOne: function (source, predicate) {
                var i, len, element;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        element = source[i];
                        if (predicate(element) === true) {
                            return element;
                        }
                    }
                }
            },

            transform: function (source, transformer) {
                var result = [],
                    i, len, element;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        element = source[i];
                        result.push(transformer(element));
                    }
                }
                return result;
            },

            allAre: function (source, predicate) {
                var i, len, element;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        element = source[i];
                        if (predicate(element) !== true) {
                            return false;
                        }
                    }
                    return true;
                }
                throw new TypeError("source 不是一个合法的数组");
            },

            doForAll: function (source, closure) {
                var i, len;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        closure(source[i]);
                    }
                }
            },

            indexOf: function (source, desc) {
                var i, len;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        if (angular.equals(source[i], desc)) {
                            return i;
                        }
                    }
                }
                return -1;
            },

            remove: function (source, desc) {
                if (angular.isArray(source)) {
                    var descIndex = this.indexOf(source, desc);
                    if (descIndex !== -1) {
                        source.splice(descIndex, 1);
                    }
                }
            },

            reduce: function (source, closure) {
                if (angular.isFunction(Array.prototype.reduce)) {
                    return Array.prototype.reduce.call(source, closure);
                }
                if (angular.isArray(source) && angular.isFunction(closure)) {
                    if (source.length === 0) {
                        throw new TypeError('Reduce of empty array with no initial value');
                    } else if (source.length === 1) {
                        return source[0];
                    } else {
                        var result = closure(source[0], source[1]);
                        angular.forEach(source.splice(2), function (value) {
                            result = closure(result, value);
                        });
                        return result;
                    }
                }
            },

            count: function (source, predicate) {
                var count = 0,
                    pred = angular.isFunction(predicate) ? predicate : function (element) {
                        return element === predicate;
                    },
                    i, len;
                if (angular.isArray(source)) {
                    for (i = 0, len = source.length; i < len; i++) {
                        if (pred(source[i]) === true) {
                            count ++;
                        }
                    }
                }
                return count;
            },

            /**
             *
             * @param source {Array} 源数组
             * @param fields {Array or String} 分组字段名称数组或单个分组名
             * @returns {} 按分组名组织的层次结构对象
             */
            group: function (source, fields) {
                var result = {},
                    i=0, len=source.length,
                    j, len2,
                    row, key, o;
                if(!angular.isArray(fields)) fields = [fields];
                len2 = fields.length;
                for(; i<len; i++) {
                    row = source[i];
                    o = result;
                    for(j=0;j<len2;j++) {
                        key = row[fields[j]];
                        if(!o[key]) {
                            o[key] = (j == len2-1)?[]:{};
                        }
                        o = o[key];
                    }
                    o.push(row);
                }
                return result;
            }
        }
    };

});